package kin.init.system;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;
import javax.persistence.Enumerated;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import kin.annotation.system.EmbeddedIdColumn;
import kin.annotation.system.ExcelField;
import kin.annotation.system.KinColumn;
import kin.annotation.system.KinForm;
import kin.annotation.system.M2OAttribute;
import kin.annotation.system.O2MAttribute;
import kin.annotation.system.O2OAttribute;
import kin.bean.security.Acl;
import kin.bean.security.Button;
import kin.bean.security.Module;
import kin.bean.security.Role;
import kin.bean.security.User;
import kin.bean.system.Form;
import kin.bean.system.M2M;
import kin.bean.system.M2O;
import kin.bean.system.O2M;
import kin.bean.system.O2O;
import kin.service.security.AclServiceManager;
import kin.service.security.ButtonServiceManager;
import kin.service.security.ModuleServiceManager;
import kin.service.security.RoleServiceManager;
import kin.service.security.UserServiceManager;
import kin.service.system.FormServiceManager;
import kin.service.system.KinColumnServiceManager;
import kin.service.system.KinFormServiceManager;
import kin.service.system.KinSearchFormServiceManager;
import kin.service.system.M2MServiceManager;
import kin.service.system.M2OServiceManager;
import kin.service.system.O2MServiceManager;
import kin.service.system.O2OServiceManager;
import kin.system.util.ClassUtil;
import kin.system.util.ScanClassesOfPackage;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

//@Component  
@Service
public class InitSystemClassRelation {
	@Inject
	private M2MServiceManager m2MServiceManager;
	@Inject
	private O2MServiceManager o2MServiceManager;
	@Inject
	private M2OServiceManager m2OServiceManager;
	@Inject
	private O2OServiceManager o2OServiceManager;
	@Inject
	private FormServiceManager formServiceManager;
	@Inject
	private KinColumnServiceManager kinColumnServiceManager;
	@Inject
	private KinFormServiceManager kinFormServiceManager;
	@Inject
	private KinSearchFormServiceManager kinSearchFormServiceManager;

	@Inject
	private UserServiceManager userServiceManager;
	@Inject
	private RoleServiceManager roleServiceManager;
	@Inject
	private ButtonServiceManager buttonServiceManager;
	@Inject
	private ModuleServiceManager moduleServiceManager;
	@Inject
	private AclServiceManager aclServiceManager;

	public void init() throws Exception {

		this.formServiceManager.deleteAll();
		this.scanEntityPackage("kin.bean.system");
		this.scanEntityPackage("kin.bean.customer");
		this.scanEntityPackage("kin.bean.security");

		User user = this.userServiceManager.findByUsername("admin");
		Role role = this.roleServiceManager.findByName("ROLE_Admin");
		if (role == null) {
			role = new Role();
			role.setRole_name("ROLE_Admin");
			this.roleServiceManager.add(role);
		}

		role = this.roleServiceManager.findByName("ROLE_Admin");
		if (user == null) {
			User u = new User();
			u.setUsername("admin");
			u.setPassword("123");
			this.userServiceManager.add(u);
			this.userServiceManager.addOrUpdateRoleByUser(u, role, 1);
		}

		// 2014-4-30
		// List<Module> newModules= null;
		// List<Acl> acls = this.aclServiceManager.findAll();

		/*
		 * ClassPathScanningCandidateComponentProvider scanner = new
		 * ClassPathScanningCandidateComponentProvider(false);
		 * scanner.addIncludeFilter(new AnnotationTypeFilter(Controller.class));
		 */

		/*
		 * this.scanModuleByPackage(scanner,
		 * "kin.controller.system",oldModules);
		 * this.scanModuleByPackage(scanner,
		 * "kin.controller.customer",oldModules);
		 */
		// newModules= this.moduleServiceManager.findAll();
		this.scanModuleByPackage("kin.controller", role);

		// addOrUpdate acls by user(admin) and role(role_admin)
		/*
		 * if (acls!=null&&acls.size() > 0) {
		 * oldModules.removeAll(this.aclServiceManager
		 * .findModulesByUser(user.getUser_id() )); for(Module m:oldModules){
		 * if(Acl.getPermissionByModule(m.getModule_url())!=-1)
		 * this.aclServiceManager.addOrUpdatePermission(Acl.TYPE_ROLE,
		 * role.getRole_id(), m.getModule_id(),
		 * Acl.getPermissionByModule(m.getModule_url()), true); } }
		 */
		// add acls by user(admin) and role(role_admin)
		/*
		 * else { if(newModules!=null&& newModules.size()>0) for (Module module
		 * : newModules) { if (module.getParent() != null) {
		 * 
		 * String sa = (module.getModule_url()).split("/")[2];
		 * if(Acl.getPermissionByModule(module.getModule_url())!=-1){
		 * this.aclServiceManager.addOrUpdatePermission( Acl.TYPE_ROLE,
		 * role.getRole_id(), module .getParent().getModule_id(),
		 * Permission.Create, true); } System.out.println("url:" +
		 * module.getModule_url()); System.out.println("sa:" + sa);
		 * this.aclServiceManager.addOrUpdatePermission( Acl.TYPE_ROLE,
		 * role.getRole_id(), module.getParent().getModule_id(),
		 * Acl.getPermissionByModule(module.getModule_url()), true);
		 * 
		 * } }
		 * 
		 * acls = this.aclServiceManager.findAll(); }
		 */
		/*
		 * ClassPathXmlApplicationContext appContext = new
		 * ClassPathXmlApplicationContext(new String[]
		 * {"applicationContext.xml", "applicationContext-part2.xml"});
		 * RequestMappingHandlerMapping rm=new RequestMappingHandlerMapping();
		 * rm.setApplicationContext(appContext);
		 * 
		 * 
		 * System.out.println("ssa:"+rm.getUrlPathHelper());
		 * System.out.println("ssb:"+rm.getPathMatcher());
		 * System.out.println("ssc:"+rm.getHandlerMethods());
		 * //WebApplicationContextUtils.getWebApplicationContext(sc);
		 * InternalResourceViewResolver ic=new InternalResourceViewResolver();
		 * Map<String,Object> ma=ic.getAttributesMap(); for (Map.Entry<String,
		 * Object> entry : ma.entrySet()) { String key =
		 * entry.getKey().toString(); String s = (String) entry.getValue();
		 * 
		 * System.out.println(key + ":::" + s);
		 * 
		 * }
		 */

		/*
		 * for(Module m:modules){
		 * 
		 * }
		 */

	}

	/*
	 * private void scanControllerPackage(String packageName){
	 * ClassPathScanningCandidateComponentProvider scanner = new
	 * ClassPathScanningCandidateComponentProvider(false);
	 * scanner.addIncludeFilter(new
	 * AnnotationTypeFilter(org.springframework.stereotype.Controller.class));
	 * for (BeanDefinition beanDefinition :
	 * scanner.findCandidateComponents(packageName)) { Class cla = null; try {
	 * cla = Class.forName(beanDefinition.getBeanClassName()); Method[]
	 * ms=cla.getMethods(); for(Method m:ms){ m.getAnnotation(annotationClass) }
	 * } catch (ClassNotFoundException e) { // TODO Auto-generated catch block
	 * e.printStackTrace(); } }
	 * 
	 * }
	 */

	private void scanEntityPackage(String packageName) {
		ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
				false);
		scanner.addIncludeFilter(new AnnotationTypeFilter(
				javax.persistence.Entity.class));

		for (BeanDefinition beanDefinition : scanner
				.findCandidateComponents(packageName)) {

			Class cla = null;
			try {
				cla = Class.forName(beanDefinition.getBeanClassName());
			} catch (ClassNotFoundException e2) {
				// TODO Auto-generated catch block
				e2.printStackTrace();
			}

			Field[] fields = cla.getDeclaredFields();
			if (fields.length > 0) {
				Form form = new Form();
				form.setForm_fullname(cla.getName());
				form.setForm_name(cla.getSimpleName());
				try {
					this.formServiceManager.add(form);
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				for (Field field : fields) {

					/*
					 * start scan
					 */
					// KinForm
					if (field.isAnnotationPresent(KinForm.class)) {
						kin.bean.system.KinForm c = new kin.bean.system.KinForm();
						KinForm s = field.getAnnotation(KinForm.class);
						if (field.isAnnotationPresent(Enumerated.class)) {
							c.setType("int");
						} else {
							c.setType((field.getType().getSimpleName())
									.toLowerCase());
						}
						if (field.getAnnotation(NotNull.class) != null) {
							c.setAllowBlank(false);
						} else {
							c.setAllowBlank(true);
						}
						if (field.getAnnotation(Size.class) != null) {
							Size size = field.getAnnotation(Size.class);
							c.setMinLength(size.min());
							c.setMaxLength(size.max());
							// c.setMinLength(s.minLength());
							// c.setMaxLength(s.maxLength());
						}
						if (field.getAnnotation(Max.class) != null)
							c.setMax(field.getAnnotation(Max.class).value());
						if (field.getAnnotation(Min.class) != null)
							c.setMin(field.getAnnotation(Min.class).value());

						c.setXtype(s.xtype());

						c.setVtype(s.vtype());
						c.setSort(s.sort());
						c.setSerializable(s.serializable());
						/*
						 * if (field.isAnnotationPresent(Enumerated.class)) {
						 * c.setFieldName(field.getName() + "_id"); } else {
						 */
						c.setFieldName(field.getName());
						// }
						c.setLabel(s.label());
						c.setHidden(s.hidden());
						c.setForm(form);
						try {
							this.kinFormServiceManager.add(c);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}

					// KinColumn
					if (field.isAnnotationPresent(KinColumn.class)) {
						KinColumn s = field.getAnnotation(KinColumn.class);
						List<kin.bean.system.KinColumn> ls=new ArrayList<kin.bean.system.KinColumn>();
						
                        if (s.isEmbeddedId() && s.embeddedIdColumns() != null&& s.embeddedIdColumns().length > 0) {
                            	Class cls=field.getType();
                            if(!ClassUtil.isBaseDataType(cls)){
                            	 for(int i=0;i<s.embeddedIdColumns().length;i++){	
                            		 kin.bean.system.KinColumn embc = new kin.bean.system.KinColumn();
                            		 EmbeddedIdColumn emb=s.embeddedIdColumns()[i];
                            		 embc.setCls(emb.cls());
									 //embc.setAllowBlank(emb.);
                            	 }
                            		
                            }
						}else{
						kin.bean.system.KinColumn c = new kin.bean.system.KinColumn();
						c.setForm(form);
						
						if (field.getAnnotation(NotNull.class) != null) {
							c.setAllowBlank(false);
						} else {
							c.setAllowBlank(true);
						}

						if (field.getAnnotation(Size.class) != null) {
							Size size = field.getAnnotation(Size.class);
							c.setMinLength(size.min());
							c.setMaxLength(size.max());
							// c.setMinLength(s.minLength());
							// c.setMaxLength(s.maxLength());
						}

						if (field.getAnnotation(Max.class) != null)
							c.setMax(field.getAnnotation(Max.class).value());
						if (field.getAnnotation(Min.class) != null)
							c.setMin(field.getAnnotation(Min.class).value());
						c.setVtype(s.vtype());
						c.setSort(s.sort());

						c.setSerializable(s.serializable());

						if (field.isAnnotationPresent(Enumerated.class)) {
							c.setType("int");
							// c.setRealType(field.getType().getName());
							c.setFieldName(field.getName()/* +"_id" */);
						} else {
							c.setType((field.getType().getSimpleName())
									.toLowerCase());
							c.setFieldName(field.getName());
						}

						c.setWidth(s.width());
						c.setHeader(s.header());
						c.setHidden(s.hidden());
						c.setXtype(s.xtype());
						ls.add(c);
						}
						
						try {
							
							for( kin.bean.system.KinColumn c:ls){
								this.getKinColumnServiceManager().add(c);
							}

							
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

						// s.harder()
					}

					// ManyToOne
					if (field.isAnnotationPresent(ManyToOne.class)
							&& field.isAnnotationPresent(M2OAttribute.class)) {
						ManyToOne tag = field.getAnnotation(ManyToOne.class);
						M2OAttribute m2otag = field
								.getAnnotation(M2OAttribute.class);
						M2O m2o = new M2O();
						m2o.setTargetEntity(field.getType().getSimpleName());
						m2o.setFull_name(field.getType().getName());
						m2o.setForm(form);
						m2o.setName(field.getName());
						m2o.setLabel(m2otag.label());
						m2o.setAllowBlank(m2otag.allowBlank());
						m2o.setXtype(m2otag.xtype());
						m2o.setComboName(m2otag.comboName());
						m2o.setSort(m2otag.sort());
						m2o.setRenderToBuildForm(m2otag.isRenderToBuildForm());
						try {
							this.m2OServiceManager.add(m2o);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}

					// ManyToMany
					if (field.isAnnotationPresent(ManyToMany.class)) {
						ManyToMany tag = field.getAnnotation(ManyToMany.class);
						M2M m2m = new M2M();
						m2m.setTargetEntity(tag.targetEntity().getSimpleName());
						m2m.setForm(form);
						m2m.setName(field.getName());
						m2m.setLabel(field.getName());
						m2m.setXtype("checkboxgroup");
						try {
							this.m2MServiceManager.add(m2m);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}

					// OneToMany
					if (field.isAnnotationPresent(OneToMany.class)
							&& field.isAnnotationPresent(O2MAttribute.class)) {
						OneToMany tag = field.getAnnotation(OneToMany.class);
						O2MAttribute o2mTag = field
								.getAnnotation(O2MAttribute.class);
						O2M o2m = new O2M();
						o2m.setRenderToBuildForm(o2mTag.isRenderToBuildForm());
						o2m.setRenderToList(o2mTag.isRenderToList());
						if (o2mTag.isRenderToList()) {
							// o2m.
						}
						o2m.setXtype(o2mTag.xtype());
						o2m.setTargetEntity(tag.targetEntity().getSimpleName());
						o2m.setForm(form);
						o2m.setName(field.getName());
						o2m.setTitle(o2mTag.title());
						o2m.setSort(o2mTag.sort());
						try {
							this.o2MServiceManager.add(o2m);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}

					// OneToOne
					if (field.isAnnotationPresent(OneToOne.class)
							&& field.isAnnotationPresent(O2OAttribute.class)) {
						O2OAttribute o2otag = field
								.getAnnotation(O2OAttribute.class);

						O2O o2o = new O2O();
						o2o.setTargetEntity(field.getType().getSimpleName());
						o2o.setFull_name(field.getType().getName());
						o2o.setForm(form);
						o2o.setName(field.getName());
						o2o.setLabel(o2otag.title());
						o2o.setXtype(o2otag.xtype());
						o2o.setSort(o2otag.sort());
						o2o.setRenderToBuildForm(o2otag.isRenderToBuildForm());
						try {
							this.o2OServiceManager.add(o2o);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}

					/*
					 * if (field.isAnnotationPresent(ExcelField.class)) {
					 * ExcelField o2otag =
					 * field.getAnnotation(ExcelField.class);
					 * 
					 * O2O o2o = new O2O();
					 * o2o.setTargetEntity(field.getType().getSimpleName());
					 * o2o.setFull_name(field.getType().getName());
					 * o2o.setForm(form); o2o.setName(field.getName());
					 * 
					 * o2o.setLabel(o2otag.title());
					 * o2o.setXtype(o2otag.xtype()); o2o.setSort(o2otag.sort());
					 * o2o.setRenderToBuildForm(o2otag.isRenderToBuildForm());
					 * try { this.o2OServiceManager.add(o2o); } catch (Exception
					 * e) { // TODO Auto-generated catch block
					 * e.printStackTrace(); } }
					 */

				}

			}
		}
	}

	public RoleServiceManager getRoleServiceManager() {
		return roleServiceManager;
	}

	public void setRoleServiceManager(RoleServiceManager roleServiceManager) {
		this.roleServiceManager = roleServiceManager;
	}

	public UserServiceManager getUserServiceManager() {
		return userServiceManager;
	}

	public void setUserServiceManager(UserServiceManager userServiceManager) {
		this.userServiceManager = userServiceManager;
	}

	public M2MServiceManager getM2MServiceManager() {
		return m2MServiceManager;
	}

	public void setM2MServiceManager(M2MServiceManager m2mServiceManager) {
		m2MServiceManager = m2mServiceManager;
	}

	public O2MServiceManager getO2MServiceManager() {
		return o2MServiceManager;
	}

	public void setO2MServiceManager(O2MServiceManager o2mServiceManager) {
		o2MServiceManager = o2mServiceManager;
	}

	public M2OServiceManager getM2OServiceManager() {
		return m2OServiceManager;
	}

	public void setM2OServiceManager(M2OServiceManager m2oServiceManager) {
		m2OServiceManager = m2oServiceManager;
	}

	public O2OServiceManager getO2OServiceManager() {
		return o2OServiceManager;
	}

	public void setO2OServiceManager(O2OServiceManager o2oServiceManager) {
		o2OServiceManager = o2oServiceManager;
	}

	public FormServiceManager getFormServiceManager() {
		return formServiceManager;
	}

	public void setFormServiceManager(FormServiceManager formServiceManager) {
		this.formServiceManager = formServiceManager;
	}

	public KinColumnServiceManager getKinColumnServiceManager() {
		return kinColumnServiceManager;
	}

	public void setKinColumnServiceManager(
			KinColumnServiceManager kinColumnServiceManager) {
		this.kinColumnServiceManager = kinColumnServiceManager;
	}

	public KinFormServiceManager getKinFormServiceManager() {
		return kinFormServiceManager;
	}

	public void setKinFormServiceManager(
			KinFormServiceManager kinFormServiceManager) {
		this.kinFormServiceManager = kinFormServiceManager;
	}

	public KinSearchFormServiceManager getKinSearchFormServiceManager() {
		return kinSearchFormServiceManager;
	}

	public void setKinSearchFormServiceManager(
			KinSearchFormServiceManager kinSearchFormServiceManager) {
		this.kinSearchFormServiceManager = kinSearchFormServiceManager;
	}

	public ButtonServiceManager getButtonServiceManager() {
		return buttonServiceManager;
	}

	public void setButtonServiceManager(
			ButtonServiceManager buttonServiceManager) {
		this.buttonServiceManager = buttonServiceManager;
	}

	// 2014-4-30 scan package @RequestMapping and @kin.annotation.system.Module
	// and @conntroller

	private void scanModuleByPackage(String packageName, Role role)
			throws Exception {
		List<Module> oldModules = this.moduleServiceManager.findAll();
		/*
		 * Set<Module> newModules=new HashSet<Module>(); Set<Button>
		 * newButtons=new HashSet<Button>();
		 */
		List<Button> oldButtons = this.buttonServiceManager.findAll();
		List<Module> newModules = new ArrayList<Module>();

		List<Button> newButtons = new ArrayList<Button>();

		ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
				true);
		Set<BeanDefinition> candidates = scanner
				.findCandidateComponents(packageName);

		for (BeanDefinition claf : candidates) {

			Class cla = Class.forName(claf.getBeanClassName());
			System.out.println("-----------" + cla.getName());

			if (cla.isAnnotationPresent(RequestMapping.class)
					&& cla.isAnnotationPresent(kin.annotation.system.Module.class)) {
				kin.annotation.system.Module pm = (kin.annotation.system.Module) cla
						.getAnnotation(kin.annotation.system.Module.class);
				RequestMapping r = (RequestMapping) cla
						.getAnnotation(RequestMapping.class);
				String path = r.value()[0];
				Module mp = new Module();
				mp.setClassName(cla.getName());
				mp.setModule_url(path);
				mp.setLeaf(false);
				mp.setRenderToNAV(pm.renderToNAV());
				mp.setLabel(pm.label());
				newModules.add(mp);
				Method[] methods = cla.getDeclaredMethods();
				for (int i = 0; i < methods.length; i++) {
					Method method = methods[i];
					boolean b = method
							.isAnnotationPresent(RequestMapping.class)
							&& method
									.isAnnotationPresent(kin.annotation.system.Module.class);
					if (b) {
						RequestMapping racc = method
								.getAnnotation(RequestMapping.class);
						kin.annotation.system.Module pc = (kin.annotation.system.Module) method
								.getAnnotation(kin.annotation.system.Module.class);
						Module module = new Module();
						module.setLabel(pc.label());
						String ppa = racc.value()[0].split("/")[1];
						module.setModule_url(path + "/" + ppa);
						String meth = "GET";
						if (racc.method().length > 0) {
							meth = (racc.method()[0].name()).toString();
						}
						module.setClassMethodName(method.getName());
						module.setClassName(cla.getName());
						module.setMethod(meth);
						module.setParent(mp);
						module.setLeaf(true);
						module.setSimpleClassName(path.split("/")[1]);
						if ("".equals(pc.view())) {
							module.setView(ppa);
						} else {
							module.setView(pc.view());
						}
						module.setRenderToNAV(pc.renderToNAV());

						if (method
								.isAnnotationPresent(kin.annotation.system.Button.class)) {

							kin.annotation.system.Button button = method
									.getAnnotation(kin.annotation.system.Button.class);
							Button btn = new Button();
							btn.setLabel(button.label());
							btn.setLabel(button.label());
							btn.setSort(btn.getSort());
							btn.setAction(ppa);
							btn.setMethod(meth);
							btn.setRenderToActionColumn(button
									.renderToActionColumn());
							btn.setRenderToList(button.renderToList());
							btn.setTargetName(module.getSimpleClassName());
							btn.setUrl(module.getModule_url());
							newButtons.add(btn);
						}
						newModules.add(module);
					}
				}
			}
		}

		for (Module m : oldModules) {
			if (newModules.contains(m)) {
				for (Module m1 : newModules) {
					if (m1.equals(m)) {
						m1.setModule_id(m.getModule_id());

					}
				}
			} else {
				if (m.getParent() == null) {

					// this.aclServiceManager.delPermission(principalType,
					// principalId, moduleId);
				} else {

					this.aclServiceManager.delPermissionsByModule(m.getParent()
							.getModule_id(), Acl.getPermissionByModule(m
							.getModule_url()));

				}
				// this.aclServiceManager.delPermission(principalType,
				// principalId, moduleId);

				this.moduleServiceManager.delete(m.getModule_id());
			}
		}

		for (Module m : newModules) {
			if (m.getParent() != null) {
				// String sa = (m.getModule_url()).split("/")[2];
				int state = Acl.getPermissionByModule(m.getModule_url());
				if (state != -1) {
					this.aclServiceManager.addOrUpdatePermission(Acl.TYPE_ROLE,
							role.getRole_id(), m.getParent().getModule_id(),
							state, true);
				}

			}
			this.moduleServiceManager.update(m);
		}

		for (Button m : oldButtons) {
			if (newButtons.contains(m)) {
				for (Button m1 : newButtons) {
					if (m1 == m) {
					}
					m1.setButton_id(m.getButton_id());
				}
			} else {
				this.buttonServiceManager.delete(m.getButton_id());
			}
		}

		for (Button m : newButtons) {
			this.buttonServiceManager.update(m);
		}

	}

}
